import os
import time
import qrcode
import jsonpickle
import marshmallow
from enum import Enum
from abc import ABC, abstractmethod

FragmentType = Enum("FragmentType", ("number", "string", "date"))
JsonType = Enum("JsonType", ("jsonpickle", "marshmallow"))


class Fragment:
    def __init__(self, length: int, type: FragmentType, string: str, name: str = ""):
        self.name = name
        self.length = length
        self.type = type
        self.string = string


class CodeTemplate:
    def __init__(
        self, fragments: list[Fragment], separators: list[str], dateFormate: str
    ):
        self.fragments = fragments
        self.separators = separators
        self.dateFormate = dateFormate

    def __str__(self):
        string = ""
        for i in range(len(self.fragments)):
            string += self.fragments[i].string
            if i < len(self.separators):
                string += self.separators[i]
        return string

    def __eq__(self, value: object) -> bool:
        if not isinstance(value, CodeTemplate):
            return False
        if len(self.fragments) != len(value.fragments):
            return False
        for i in range(len(self.fragments)):
            if self.fragments[i].length != value.fragments[i].length:
                return False
            if self.fragments[i].type != value.fragments[i].type:
                return False
        for i in range(len(self.separators)):
            if self.separators[i] != value.separators[i]:
                return False
        return True

    def isChildren(self, value: object) -> bool:
        if not isinstance(value, Code):
            return False
        if len(self.fragments) != len(value.fragments):
            return False
        for i in range(len(value.fragments)):
            if self.fragments[i].length != value.fragments[i].length:
                return False
            if self.fragments[i].type != value.fragments[i].type:
                return False
        return True


class Code(CodeTemplate):

    def __init__(
        self, fragments: list[Fragment], separators: list[str], dateFormate: str
    ):
        super().__init__(fragments, separators, dateFormate)
        self.__addTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())

    @property
    def addTime(self):
        return self.__addTime

    def getQRCode(self):
        # qr = qrcode.QRCode(
        #     error_correction=qrcode.constants.ERROR_CORRECT_H,
        #     box_size=10,
        #     border=1,
        # )
        qr = qrcode.QRCode(
            version=3,
            error_correction=qrcode.constants.ERROR_CORRECT_H,
            box_size=10,
            border=1,
        )
        qr.add_data(str(self))
        qr.make(fit=True)
        img = qr.make_image(fill_color="black", back_color="white")

        return img

    def __eq__(self, value: object) -> bool:
        if isinstance(value, Code):
            return str(self) == str(value)

        if isinstance(value, str):
            return str(self) == value

        if isinstance(value, CodeTemplate):
            return super().__eq__(value)

        return False


class Data(ABC):
    def __init__(self):
        self.codeList = list[Code]()
        self.templateList = list[Code]()
        pass

    @abstractmethod
    def addCode(self):
        pass

    @abstractmethod
    def searchCode(self):
        pass

    @abstractmethod
    def deleteCode(self):
        pass

    @abstractmethod
    def addTemplate(self):
        pass

    @abstractmethod
    def searchTemplate(self):
        pass

    @abstractmethod
    def deleteTemplate(self):
        pass

    def CodeList(self):
        return self.codeList

    def TemplateList(self):
        return self.templateList


class DataJson(Data):

    def __init__(self, path: str, jsonType: JsonType = JsonType.jsonpickle):
        self.path = path
        self.jsonType = jsonType
        self.dataReader = self.GetDataReaderByType(jsonType)
        self.data = self.dataReader.readDataFromPath(path)
        self.codeList = self.data["codeList"]
        self.templateList = self.data["templateList"]

    @staticmethod
    def initData():
        return {"codeList": list[Code](), "templateList": list[CodeTemplate]()}

    @staticmethod
    def GetDataReaderByType(jsonType: JsonType):
        if jsonType == JsonType.jsonpickle:
            return JsonpickleReader()
        else:
            return None

    def save(self):
        self.dataReader.writeDataToPath(self.path, self.data)

    def addTemplate(self, template: CodeTemplate):
        self.templateList.append(template)
        self.save()

    def addCode(self, code: Code):
        self.codeList.append(code)
        self.save()

    def searchCode(self, code):
        for codeData in self.codeList:
            if codeData == code:
                return codeData
        return None

    def searchTemplate(self, template):
        for templateData in self.templateList:
            if templateData == template:
                return templateData
        return None

    def deleteCode(self, code):
        codeData = self.searchCode(code)
        if codeData is not None:
            self.codeList.remove(codeData)
            self.save()
            return True
        return False

    def deleteTemplate(self, template):
        templateData = self.searchTemplate(template)
        if templateData is not None:
            self.templateList.remove(templateData)
            self.save()
            return True
        return False


class DataReader:
    @abstractmethod
    def readDataFromPath(self, path: str):
        pass

    @abstractmethod
    def encode(self, data):
        pass

    def checkData(self, data):
        if isinstance(data, dict):
            if "codeList" not in data:
                data["codeList"] = list[Code]()
            if "templateList" not in data:
                data["templateList"] = list[CodeTemplate]()
        else:
            return DataJson.initData()
        return data


class JsonpickleReader(DataReader):
    def readDataFromPath(self, path: str):
        jsonString = ""
        if not os.path.exists(path):
            with open(path, "w") as file:
                file.write("")
        with open(path, "r") as file:
            jsonString = file.read()
        if jsonString == "":
            return DataJson.initData()
        data = jsonpickle.decode(jsonString)
        data = self.checkData(data)
        return data

    def writeDataToPath(self, path: str, data):
        jsonString = jsonpickle.encode(data)
        with open(path, "w") as file:
            file.write(jsonString)
